package dao

import (
	rentalpb "car_rental/rental/api/gen/v1"
	"car_rental/shared/id"
	mgutil "car_rental/shared/mongo"
	"car_rental/shared/mongo/objid"
	"context"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

const (
	tripField      = "trip"
	accountIdField = tripField + ".accountid"
	statusField    = tripField + ".status"
)

// Mongo MongoDao
type Mongo struct {
	col *mongo.Collection
}
type TripRecord struct {
	mgutil.IdField        `bson:"inline"`
	mgutil.UpdatedAtField `bson:"inline"`
	*rentalpb.Trip        `bson:"trip"`
}

// NewMongo 创建一个MongoDao
func NewMongo(db *mongo.Database) *Mongo {
	return &Mongo{
		col: db.Collection("trip"),
	}
}

// CreateTrip 创建Trip
func (m *Mongo) CreateTrip(ctx context.Context, trip *rentalpb.Trip) (*TripRecord, error) {

	r := &TripRecord{
		Trip: trip,
	}

	r.Id = mgutil.NewObjId()

	r.UpdatedAt = mgutil.UpdatedAt()

	_, err := m.col.InsertOne(ctx, r)

	if err != nil {
		return nil, err
	}

	return r, nil

}

// GetTrip 根据ID查询
func (m *Mongo) GetTrip(ctx context.Context, id id.TripId, accountId id.AccountId) (*TripRecord, error) {

	objId, err := objid.FromId(id)

	if err != nil {
		return nil, fmt.Errorf("无效ID: %v", id)
	}

	res := m.col.FindOne(ctx, bson.M{
		mgutil.IdFieldName: objId,
		accountIdField:     accountId,
	})

	if err := res.Err(); err != nil {
		return nil, err
	}

	var trip TripRecord

	err = res.Decode(&trip)

	if err != nil {
		return nil, fmt.Errorf("无法解析: %v", err)
	}

	return &trip, nil

}

// GetTrips 根据状态获取Trip
func (m *Mongo) GetTrips(ctx context.Context, accountId id.AccountId, status rentalpb.TripStatus) ([]*TripRecord, error) {

	filter := bson.M{
		accountIdField: accountId.String(),
	}

	if status != rentalpb.TripStatus_TS_NOT_SPECIFIED {
		filter[statusField] = status
	}

	res, err := m.col.Find(ctx, filter)

	if err != nil {
		return nil, err
	}

	var trips []*TripRecord

	for res.Next(ctx) {

		var trip TripRecord

		err := res.Decode(&trip)

		if err != nil {
			return nil, err
		}

		trips = append(trips, &trip)

	}

	return trips, nil

}

// UpdateTrip 更新Trip
func (m *Mongo) UpdateTrip(ctx context.Context, tid id.TripId, aid id.AccountId, updatedAt int64, trip *rentalpb.Trip) error {

	objId, err := objid.FromId(tid)

	if err != nil {
		return fmt.Errorf("无效Id: %v", err)
	}

	newUpdatedAt := mgutil.UpdatedAt()

	res, err := m.col.UpdateOne(ctx, bson.M{
		mgutil.IdFieldName:        objId,
		accountIdField:            aid.String(),
		mgutil.UpdatedAtFieldName: updatedAt,
	}, mgutil.Set(bson.M{
		tripField:                 trip,
		mgutil.UpdatedAtFieldName: newUpdatedAt,
	}))

	if err != nil {
		return err
	}

	if res.MatchedCount == 0 {
		return mongo.ErrNoDocuments
	}

	return nil

}
